﻿using System;
using System.Collections.Generic;

namespace NodeBasedSkillEditor.Model
{
    // ContextId本质上是NodeId或PortGlobalId
    public class ContextMap
    {
        public Dictionary<uint, uint> _portGlobalId2ContextId = new Dictionary<uint, uint>();
        public Dictionary<uint, ContextGroup> _contextGroups = new Dictionary<uint, ContextGroup>();

        public uint GetPortContextId(uint portGlobalId)
        {
            uint result;
            if (_portGlobalId2ContextId.TryGetValue(portGlobalId, out result))
            {
                return result;
            }
            else
            {
                return DAGHelper.EmptyContextId;
            }
        }

        public ContextGroup TryAddContextGroup(uint contextId)
        {
            ContextGroup cgp;
            if (!_contextGroups.TryGetValue(contextId, out cgp))
            {
                cgp = new(contextId);
                _contextGroups.Add(contextId, cgp);
            }
            return cgp;
        }

        /// <summary>
        /// 尝试阐述一个ContextGroup，注意，只有无上下文源头的ContextGroup允许被删除
        /// </summary>
        /// <param name="contextId"></param>
        public void TryRemoveContextGroup(uint contextId)
        {
            ContextGroup cgp;
            if (_contextGroups.TryGetValue(contextId, out cgp))
            {
                if (cgp.SourceCount == 0)
                {
                    if (cgp.SpreadsToPorts.Count > 0)
                    {
                        throw new Exception($"ContextGroup {contextId} 没有源头，却存在被传播的端口");
                    }
                    _contextGroups.Remove(contextId);
                }
            }
        }

        public void ClearContextSpreadedPorts(uint contextId)
        {
            var cgp = GetContextGroup(contextId);

            if (cgp != null)
            {
                foreach (var pId in cgp.SpreadsToPorts)
                {
                    _portGlobalId2ContextId.Remove(pId);
                }
                cgp.SpreadsToPorts.Clear();
            }
        }

        public void ClearContextGroup(uint contextId)
        {
            var cgp = GetContextGroup(contextId);

            if (cgp != null)
            {
                foreach (var pId in cgp.SourcePorts)
                {
                    _portGlobalId2ContextId.Remove(pId);
                }
                foreach (var pId in cgp.SpreadsToPorts)
                {
                    _portGlobalId2ContextId.Remove(pId);
                }
                cgp.SourcePorts.Clear();
                cgp.SpreadsToPorts.Clear();
            }
        }

        public void ClearZeroSourceContextGroups()
        {
            HashSet<uint> zeroSourceContextId = new();
            foreach (var cgpIter in _contextGroups)
            {
                var contextId = cgpIter.Key;
                var contextGroup = cgpIter.Value;
                if (contextGroup.SourceCount == 0)
                {
                    if (contextGroup.SpreadsToPorts.Count > 0)
                    {
                        throw new Exception($"ContextGroup {contextId} 没有源头，却存在被传播的端口");
                    }

                    zeroSourceContextId.Add(contextId);
                }
            }

            foreach (var contextId in zeroSourceContextId)
            {
                _contextGroups.Remove(contextId);
            }
        }

        public void PickContextSources(uint contextId, HashSet<uint> sourcePorts)
        {
            sourcePorts.Clear();
            var cgp = GetContextGroup(contextId);
            if (cgp != null)
            {
                sourcePorts.EnsureCapacity(cgp.SourcePorts.Count);
                foreach (var pId in cgp.SourcePorts)
                {
                    sourcePorts.Add(pId);
                }
            }
        }

        public void AddContextSource(uint contextId, uint portGlobalId)
        {
            _portGlobalId2ContextId[portGlobalId] = contextId;

            ContextGroup cgp = TryAddContextGroup(contextId);
            cgp.SourcePorts.Add(portGlobalId);
        }

        public void AddContextSources(uint contextId, IEnumerable<uint> portGlobalIds)
        {
            ContextGroup cgp = TryAddContextGroup(contextId);

            foreach (var pId in portGlobalIds)
            {
                _portGlobalId2ContextId[pId] = contextId;
                cgp.SourcePorts.Add(pId);
            }
        }

        public void RemoveContextSource(uint portGlobalId)
        {
            uint contextId;
            if (_portGlobalId2ContextId.TryGetValue(portGlobalId, out contextId))
            {
                var cgp = GetContextGroup(contextId);
                if (cgp != null)
                {
                    cgp.SourcePorts.Remove(portGlobalId);
                }
                _portGlobalId2ContextId.Remove(portGlobalId);
            }
        }

        public void AddContextSpreadedPort(uint contextId, uint portGlobalId)
        {
            _portGlobalId2ContextId[portGlobalId] = contextId;

            ContextGroup cgp = TryAddContextGroup(contextId);
            cgp.SpreadsToPorts.Add(portGlobalId);
        }

        public void AddContextSpreadedPorts(uint contextId, IEnumerable<uint> portGlobalId)
        {
            ContextGroup cgp = TryAddContextGroup(contextId);

            foreach (var pId in portGlobalId)
            {
                _portGlobalId2ContextId[pId] = contextId;
                cgp.SpreadsToPorts.Add(pId);
            }
        }

        public void RemoveContextSpreadedPorts(uint portGlobalId)
        {
            uint contextId;
            if (_portGlobalId2ContextId.TryGetValue(portGlobalId, out contextId))
            {
                var cgp = GetContextGroup(contextId);
                if (cgp != null)
                {
                    cgp.SpreadsToPorts.Remove(portGlobalId);
                }
                _portGlobalId2ContextId.Remove(portGlobalId);
            }
        }

        private ContextGroup GetContextGroup(uint contextId)
        {
            ContextGroup cgp;
            _contextGroups.TryGetValue(contextId, out cgp);
            return cgp;
        }

    }

    public class ContextGroup
    {
        public readonly uint ContextId;

        public HashSet<uint> SourcePorts = new HashSet<uint>();
        public HashSet<uint> SpreadsToPorts = new HashSet<uint>();

        public int SourceCount { get => SourcePorts.Count; }

        public ContextGroup(uint contextId)
        {
            ContextId = contextId;
        }

    }
}
